package com.xiaojie.importdata.handler.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.xiaojie.context.DataInfoContext;
import com.xiaojie.importdata.handler.AbstractReplaceFieldValueHandler;
import com.xiaojie.parse.model.DataModel;
import com.xiaojie.parse.model.Settings;
import com.xiaojie.parse.model.Table;
import com.xiaojie.toolkits.FieldNameUtil;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * 替换表间关联字段值
 * 例：A表的ref_id 关联 B表的id ，导入时B表id重新生成，
 * 则A表的ref_id需要替换B表id新值
 *
 * @ClassName ReplaceRefFieldValue
 * @Author wangye
 * @Date 2022/3/13
 **/
public class ReplaceRefFieldValue extends AbstractReplaceFieldValueHandler {


    @Override
    public void replaceFieldValue(String tableName, Table table, Map rowData) {
        if (CollectionUtil.isNotEmpty(table.getRefTables())) {

            DataModel dataModel = DataInfoContext.get().getDataModelInfo();

            Map<String, Set<String>> refFieldsMap = dataModel.getRefFieldsMap();

            Settings globalSettings = dataModel.getSettings();

            Map<String, Map<String, Map>> refFieldDataMap = DataInfoContext.get().getRefFieldValueMap();
            //需要保留旧数据与新数据对应关系的字段 （优先是表里设置的
            List<String> correspondDataFieldNames = getCorrespondFieldNames(table.getSettings(), globalSettings);

            //保留多个字段旧数据与新数据对应关系
            Map<String, Map> correspondFieldValue = Optional.ofNullable(
                    DataInfoContext.get().getCorrespondDataTableFieldMap().get(tableName)
            ).orElse(Maps.newHashMap());

            //根据refTables 里设置的 srcFiled 和 refField 进行替换（不同关联表，替换的字段可能不同）
            table.getRefTables().forEach(refTable -> {
                //关联数据 表 map
                Map<String, Map> cTable = refFieldDataMap.get(refTable.getTableName());
                //新增 有可能关联的某张表没有数据
                if (cTable != null) {
                    //关联字段
                    String refField = refTable.getRefField();
                    //关联数据 <字段名 , <旧数据，新数据>>
                    // 获取关联字段数据 空map获取的可能是 null 「下划线式」字段名 数据没有的话，获取「驼峰式」字段名 关联数据
                    Map refDataMap = cTable.get(refField);
                    refDataMap = refDataMap == null ? cTable.get(StrUtil.toCamelCase(refField)) : refDataMap;
                    //lk新增 如果关联数据没有，则表示关联字段未被替换，不用替换
                    if (refDataMap != null) {
                        // 获取旧字段数据 (一样的字段 判断 「下划线式」「驼峰式」
                        Object oldFieldValue = rowData.get(FieldNameUtil.convert(refTable.getSrcField()));
                        oldFieldValue = oldFieldValue == null ? rowData.get(StrUtil.toCamelCase(refTable.getSrcField())) : oldFieldValue;
                        if (oldFieldValue != null) {
                            //获取字段名 - 判断rowData里字段名是「下划线式」还是「驼峰式」
                            String fieldName = FieldNameUtil.convert(refTable.getSrcField());
                            //旧值 换 新值 ， 如果关联数据里没有旧值则不替换
                            Object refFiledValue = raGetRefFiledValue(refDataMap, oldFieldValue);
                            if (!oldFieldValue.equals(refFiledValue)) {
                                //数据关联字段替换新值
                                rowData.put(fieldName, refFiledValue);
                                //保留字段新旧值对应关系
                                if (correspondDataFieldNames.contains(fieldName)) {
                                    Map map = correspondFieldValue.computeIfAbsent(fieldName, k -> Maps.newHashMap());
                                    map.put(oldFieldValue, rowData.get(fieldName));
                                }
                                //如果被关联
                                buildFieldRefDataMap(refFieldDataMap, tableName, oldFieldValue, refFieldsMap, fieldName, refFiledValue);
                            }
                        }

                    }
                }
            });
            // 上下文中只保存了该表的所有 「新旧对照关系」
            //将新旧数据对应关系保存在上下文中
            DataInfoContext.get().getCorrespondDataTableFieldMap().put(tableName, correspondFieldValue);
        }
    }


    /**
     * 递归获取关联表值 ，被关联字段A可能会配置了多种替换本字段值的方式
     * 如：参数替换、自定规则生成等，多种替换产生的新旧对应关系都会保留，
     * 为了获取A字段原始值与最新值的对应关系需要递归获取
     *
     * @param refDataMap
     * @param oldFieldValue
     * @return
     */
    private Object raGetRefFiledValue(Map refDataMap, Object oldFieldValue) {
        Object newOldValue = refDataMap.get(oldFieldValue);
        if (newOldValue == null) {
            return oldFieldValue;
        } else {
            return raGetRefFiledValue(refDataMap, newOldValue);
        }
    }


}
